package cn.edu.lingnan.tools;

import cn.edu.lingnan.service.redis.RedisService;
import cn.hutool.core.date.DateUtil;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;


@Component
public class DataChe {

    @Resource
    RedisService redisService;

    private Map<Long,String> dataMap = new HashMap<>();

    //初始化数据
    @PostConstruct
    public void init(){
        dataMap.put(1L,"广州");
        dataMap.put(2L,"深圳");
        dataMap.put(3L,"上海");
        dataMap.put(4L,"湛江");
    }



    /**
     * 查询
     *  如果数据没有缓存，那么dataMap里面获取，并缓存，并且将缓存的数据存储
     *
     * @Cacheable属性：
     *    1、cacheNames/value: 指定缓存组件的名字
     *    2、key: 缓存数据使用的key
     *    3、keyGenerator: 作用与key一样，二选一
     *    4、cacheManager: 缓存管理器
     *    5、cacheResolver: 缓存解析器
     *    6、condition: 指定缓存条件   Eg: condition = "#id>3" 传入的参数id>3才缓存数据
     *    7、unless: 否定缓存，当unless = "true" 不缓存
     *
     *
     */

    @Cacheable(value = "cacheDemo",key = "#id + '_dataMap'")
    public String query(Long id){
        System.out.println("当前时间：" + DateUtil.now()+"查询id的值为："+id);
        return dataMap.get(id);
    }

    /**
     *更新
     *  更新值到数据库中，并且缓存更新缓存中存储的值
     * @CachePut(): 主要针对方法配置，能够根据方法的请求参数对其结果进行更新缓存数据
     * 更新操作做注解中的key和value必须与要更新的缓存相同
     */
    @CachePut(value = "cacheDemo",key = "#id + '_dataMap'")
    public String put(Long id, String value){
        System.out.println("当前时间：" + DateUtil.now()+"更新id的值为："+id);
        dataMap.put(id,value);
        return value;
    }

    /**
     * 删除
     *  删除dataMap里面的数据
     *  并且删除cacheDme中的数据
     * @CacheEvict 删除所有缓存数据
     *  1、key: 指定要清除缓存中的某条数据
     *  2、allEntries = true : 删除缓存中的所有数据
     *  3、beforeInvocation = true : 在方法执行之前执行清除缓存
     *  4、beforeInvocation = false : 在方法执行之后清除缓存
     *      beforeInvocation = true
     *      使用在方法执行之前的好处
     *          如果方法出现异常，缓存依旧会被删除
     *
     *      1、缓存击穿 =====> 大量并发同时查询一个正好过期的数据
     *      2、缓存雪崩 =====> 大量的key同时过期
     *      3、缓存穿透 =====> 查询一个null数据
     */
    @CacheEvict(value = "cacheDemo",beforeInvocation = true)
    public void remove(Long id){
        System.out.println("当前时间：" + DateUtil.now() + "更新id的值为：" + id );
        dataMap.remove(id);
    }


    /**
     * Spring Data Redis
     * spring-data-redis是spring-data模块的一部分，用来支持spring管理项目对redis的操作，
     *      使用java操作redis最常用的是jedis，但并不是只有jedis,还是jdbc-redis、jredis，
     *      这些都是redis的java客户端
     *
     * spring data redis提供了客户端的抽象，在开发中可以忽略具体的客户端操作带来的影响，
     *      其本身就是spring生态的一部分，比起jedis更加方便，更加稳定。
     *
     *
     * 特征
     * 1．自动管理连接池
     * 2.提供了一个高度封装的RedisTemplate类
     * 3.针对jedis客户端的大量api进行了归类封装，把同一类型的操作封装成了operation接口，支持redis中的五种数据类型(String、hash、lit. set、zset) ,
     * 4.针对数据的“序列化和反序列化"，提供了多种可以选择的策略（RedisSerializer)
         * a、JdkSerializationRedisSerializer:存储java对象
         * b、StringRedisSerializer:字符串
         * c、Jackson2]sonRedisSerializer︰将对象序列化成json的格式存储在redis中，需要jackson-json jar的支持
     * 5．在Spring data redis中封装了对标redis中5种数据类型操作的方法
         * 1. opsForValue: 对应String (字符串)
         * 2. opsForZSet: 对应zset(有序集合)
         * 3. opsForHash: 对应hash(哈希)
         * 4. opsForList: 对应list(列表)
     *     5.opsForSet:   对应set(有序）
     *
     *
     *
     * 数据的序列化

        程序中数据的传输是以自己的方式进行，所有存储数据的读写操作必须是要进行序列化操作。
            序列化：把对象转化为可传输的字节序列过程称为序列化
            反序列化：把自己序列还原为对象的过程称为反序列化

        序列的目的： 跨平台、传输

        在java中，一切皆对象，而将对象的状态信息转化为存储的形式需要序列化
     *
     */

    /**
     * redis 计数器
     *  在实际开发过程中产品组给定的需求：有规律的订单号
     *  Eg：
     *      订单号格式：20220108DD0000001、 20220108DD0000002、 20220108DD0000003
     *                20220109DD0000001、 20220109DD0000002、 20220109DD0000003
     */
    public String getOrderCode(String type){
        SimpleDateFormat sdfm = new SimpleDateFormat("yyyyMMdd");
        String curDate = sdfm.format(System.currentTimeMillis());
        //将生成的订单数存储到redis中
        Long number = redisService.incr(type + curDate, 1L);
        String orderCode = padRight(number.toString(), 7, "0");
        return curDate+orderCode;

    }

    /**
     * 右补位，左对齐
     * @param oriSr 原字符串
     * @param len 目标字符串位数
     * @param ale 补位字符
     */
    public String padRight(String oriSr,int len,String ale){

        String str = "";
        int strLen = oriSr.length();
        if (strLen < oriSr.length()){
            for (int x = 0; x < len-strLen; x++ ){
                str += ale;
            }
        }
        return str + oriSr;
    }




}
